/**
 * 阿里云ddns，使用方法参考README
 * @author kangkang
 * @date 2021-12-26
 * @gitee https://gitee.com/god007/z4-ddns
 * 
 * 
============Quantumultx===============
[task_local]
0 *\/3 * * * ? aliyun-ddns.js, tag=阿里云ddns, img-url=, enabled=true

================Loon==============
[Script]
cron "0 *\/3 * * * ?" script-path=aliyun-ddns.js, tag=阿里云ddns

===============Surge=================
阿里云ddns = type=cron,cronexp="0 *\/3 * * * ?",wake-system=1,timeout=3600,script-path=aliyun-ddns.js

============小火箭=========
阿里云ddns = type=cron,script-path=aliyun-ddns.js, cronexpr="0 *\/3 * * * ?", timeout=3600, enable=true

 */
import Alidns20150109, * as $Alidns20150109 from '@alicloud/alidns20150109';
// 依赖的模块可通过下载工程中的模块依赖文件或右上角的获取 SDK 依赖信息查看
import OpenApi, * as $OpenApi from '@alicloud/openapi-client';
import * as $tea from '@alicloud/tea-typescript';
import Util from '@alicloud/tea-util';
import * as https from "https"
import * as http from "http"

export class PushUtil {

  static urlencode (str: string) {  
    str = (str + '').toString();   
    return encodeURIComponent(str).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28').  
    replace(/\)/g, '%29').replace(/\*/g, '%2A').replace(/%20/g, '+');  
  }


  static html_template = "<td width=\"720\" height=\"32\" colspan=\"2\"><table width=\"95%\" cellspacing=\"0\" cellpadding=\"0\" bordercolor=\"#666666\" border=\"0\" bgcolor=\"#FFFFFF\" style=\"font-size:12px;font-weight:normal;border-collapse:collapse;\"><tbody><tr style=\"background-color:#e4e4e4;\"><th width=\"12%\" style=\"border:1px solid #ccc; height:32px;\">类型</th><th width=\"20%\" style=\"border:1px solid #ccc; height:32px;\">记录值</th><th width=\"\" style=\"border:1px solid #ccc; height:32px;\">ip地址</th></tr>IP4_LINE IP6_LINE</tbody></table></td><p style=\"font-size:12px;font-weight:normal;\"> 本推送来源: <a href='https://gitee.com/god007/z4-ddns.git'>z4-ddns</a> by <a href=\"https://www.chenweikang.top/\">左手代码右手诗</a></p>";
  static ip4_template = "<tr><td style=\"border:1px solid #ccc;word-break:break-all;padding:4px\">IPV4</td><td style=\"border:1px solid #ccc;word-break:break-all;padding:4px\">IP4_KEY</td><td style=\"border:1px solid #ccc;word-break:break-all;padding:4px\">修改前: IP4_CHANGE_BEFORE<br>修改后: IP4_CHANGE_AFTER</td></tr>";
  static ip6_template = "<tr><td style=\"border:1px solid #ccc;word-break:break-all;padding:4px\">IPV6</td><td style=\"border:1px solid #ccc;word-break:break-all;padding:4px\">IP6_KEY</td><td style=\"border:1px solid #ccc;word-break:break-all;padding:4px\"> 修改前: IP6_CHANGE_BEFORE<br>修改后: IP6_CHANGE_AFTER</td></tr>";

  static pushplus_api = "http://www.pushplus.plus/send"
  static ftqq_api = "https://sctapi.ftqq.com"

  static pushConfigs = [
      {
       type: function(){
         return "pushplus"
       },
       getApi: function(){
         return PushUtil.pushplus_api
       },
       push: function (title: string, content : string) {
         /**
          * # PUSH_PLUS_TOKEN = "" # pushplud 的token 
          * # PUSH_KEY="" # 方糖的key
          */
          let token = process.env.PUSH_PLUS_TOKEN ? process.env.PUSH_PLUS_TOKEN : ""
          if(token == ""){
            console.log("处理推送通知---请在环境变量填写参数:PUSH_PLUS_TOKEN")
            return
          }
         //http://www.pushplus.plus/send?token=xx&title=XXX&content=XXX&template=html
         let api = PushUtil.pushplus_api + "?token=" + token + "&title=" + PushUtil.urlencode(title) + "&content=" + PushUtil.urlencode(content) + "&template=html"
         PushUtil.httpGet(api)
       },
    },
    {
      type: function(){
        return "ftqq"
      },
       getApi: function(){
          return PushUtil.ftqq_api
       },
       push: function (title: string, content : string) {
         let token = process.env.PUSH_KEY ? process.env.PUSH_KEY : ""
         if(token == ""){
          console.log("处理推送通知---请在环境变量填写参数:PUSH_KEY")
          return
         }
        let api = PushUtil.ftqq_api + "/" + token + ".send?title=" + PushUtil.urlencode(title) + "&desp="+PushUtil.urlencode(content)
        PushUtil.httpGet(api)
       },
    },
  ]

  static httpGet(url : string){
    console.log("处理推送通知----开始:"+url)
    let data = ""
    let req = null;
    if(url.startsWith("https")){
      req = https.get(url, function(res) {
        res.on('data', function(chunk) {
            data += chunk;
        });
        res.on('end', function() {
            console.log("处理推送通知----结果:"+data)
        });
      });
    } else {
      req = http.get(url, function(res) {
        res.on('data', function(chunk) {
            data += chunk;
        });
        res.on('end', function() {
            console.log("处理推送通知----结果:"+data)
        });
      });
    }
    req.on('error', (e) => {
        console.log("处理推送通知----异常:"+e)
    });
    req.end();
  }
}


export default class Client {

  static matchIp4(input: string){
    var reg = /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/g
    var math = input.match(reg);
    return math ? math[0] : ""
  }

  static matchIp6(input: string){
    var reg = /([0-9a-z]*:{1,4}){1,7}[0-9a-z]{1,4}/g
    var math = input.match(reg);
    return math ? math[0] : ""
  }

  /**
   * 使用AK&SK初始化账号Client
   * @param accessKeyId
   * @param accessKeySecret
   * @return Client
   * @throws Exception
   */
  static createClient(accessKeyId: string, accessKeySecret: string): Alidns20150109 {
    let config = new $OpenApi.Config({
      // 您的AccessKey ID
      accessKeyId: accessKeyId,
      // 您的AccessKey Secret
      accessKeySecret: accessKeySecret,
    });
    // 访问的域名
    config.endpoint = "alidns.cn-hangzhou.aliyuncs.com";
    return new Alidns20150109(config);
  }

  /**
   * 获取ipv4
   * @returns Promise Object
   */
  static getIpv4(){
    let DDNS_IP4_API = "https://myip.ipip.net"
    if (process.env.DDNS_IP4_API) {
      DDNS_IP4_API = process.env.DDNS_IP4_API
    }
    console.log("处理ipv4----获取公网ipv4-开始:"+DDNS_IP4_API)
    let data = '';
    return new Promise(function (resolve, reject) {
      let req = https.get(DDNS_IP4_API, function(res) {
          res.on('data', function(chunk) {
              data += chunk;
          });
          res.on('end', function() {
              let ip4 = Client.matchIp4(data);
              console.log("处理ipv4----获取公网ipv4-结果:"+ip4)
              resolve({
                result: ip4 != "" ? true : false,
                data: ip4
              });
          });
      });
      req.on('error', (e) => {
          resolve({
            result:false,
            data:""
          });
      });
      req.end();
    });
  }

  /**
   * 获取ipv6
   * @returns Promise Object
   */
  static getIpv6(){
    let DDNS_IP6_API = "https://api-ipv6.ip.sb/ip"
    if (process.env.DDNS_IP6_API) {
      DDNS_IP6_API = process.env.DDNS_IP6_API
    }
    console.log("处理ipv6----获取公网ipv6-开始:"+DDNS_IP6_API)
    let data = '';
    return new Promise(function (resolve, reject) {
      let req = https.get(DDNS_IP6_API, function(res) {
          res.on('data', function(chunk) {
              data += chunk;
          });
          res.on('end', function() {
              let ip6 = Client.matchIp6(data);
              console.log("处理ipv6----获取公网ipv6-结果:"+ip6)
              resolve({
                result: ip6 != "" ? true : false,
                data: ip6
              });
          });
      });
      req.on('error', (e) => {
          resolve({
            result: true,
            data: ""
          });
      });
      req.end();
    });
  }

  /**
   * 
   * @param client sdk客户端
   * @param ipv4Domain ipv4解析域名
   */
  static handleIpv4(client : Alidns20150109, ipv4Domain : String){
    let key = ipv4Domain.substring(0,ipv4Domain.indexOf("."));
    let domain = ipv4Domain.substring(ipv4Domain.indexOf(".")+1);
    //获取ipv4
    let changeResult =  { domain: "", change: false, before: "", after: "", rr: "" }
    this.getIpv4().then(async function(res: any){
      let ip = res.data
      if(res.result){
        let describeDomainRecordsRequest = new $Alidns20150109.DescribeDomainRecordsRequest({
          domainName: domain,
          typeKeyWord: "A",
          RRKeyWord: key,
        });
        console.log("处理ipv4----查询域名解析记录-开始:"+Util.toJSONString($tea.toMap(describeDomainRecordsRequest)))
        let resp = await client.describeDomainRecords(describeDomainRecordsRequest);
        //console.log(Util.toJSONString($tea.toMap(resp)));
        let queryResult = Util.toJSONString(resp.body.domainRecords)
        let resultObj = JSON.parse(queryResult);
        let domanResult = resultObj.record[0]
        console.log("处理ipv4----查询域名解析记录-结果:"+queryResult);

        changeResult.domain = domain
        changeResult.rr = key

        if(domanResult){
          //更新域名记录
          changeResult.before = domanResult.value
          changeResult.after = ip
          let updateDomainRecordRequest = new $Alidns20150109.UpdateDomainRecordRequest({
            TTL: 600,
            priority: 1,
            value: ip,
            type: "A",
            RR: key,
            recordId: domanResult.recordId,
          });
          console.log("处理ipv4----更新域名解析记录-开始:"+Util.toJSONString($tea.toMap(updateDomainRecordRequest)))
          try {
            let updateResp = await client.updateDomainRecord(updateDomainRecordRequest);
            changeResult.change = true
            console.log("处理ipv4----更新域名解析记录-完成:"+Util.toJSONString($tea.toMap(updateResp)))
          } catch (error) {
            console.log("处理ipv4----更新域名解析记录-完成:本次ip没有改变:"+error)
          }
          
        } else {
          //尝试新增记录
          let addDomainRecordRequest = new $Alidns20150109.AddDomainRecordRequest({
            TTL: 600,
            priority: 1,
            value: ip,
            type: "A",
            domainName: domain,
            RR: key,
          });
          let addResp = await client.addDomainRecord(addDomainRecordRequest);
          changeResult.before = "无"
          changeResult.after = ip
          changeResult.change = true
          console.log("处理ipv4----新增域名解析记录-完成:"+Util.toJSONString($tea.toMap(addResp)))
        }
        //console.log("处理ipv4--变更结果:"+Util.toJSONString(changeResult))
        Client.pushNotice(changeResult,null);
      } else {
        console.error("处理ipv4----ipv4获取异常,跳过处理ipv4!")
      }
      //这里用传过来的数据做其他操作
  });

    
  }

  /**
   * 处理ipv6
   * @param client sdk客户端
   * @param ipv6Domain ip6设置的域名
   */
  static handleIpv6(client : Alidns20150109,ipv6Domain : String){
    let key = ipv6Domain.substring(0,ipv6Domain.indexOf("."));
    let domain = ipv6Domain.substring(ipv6Domain.indexOf(".")+1);
    //获取ipv6
    let changeResult =  { domain: "", change: false, before: "", after: "", rr: "" }
    
    this.getIpv6().then(async function(res: any){
      let ipv6 = res.data;
      if(res.result){
        let describeDomainRecordsRequest = new $Alidns20150109.DescribeDomainRecordsRequest({
          domainName: domain,
          typeKeyWord: "AAAA",
          RRKeyWord: key,
          recordId: "1",
        });
        console.log("处理ipv6----查询域名解析记录-开始:"+Util.toJSONString($tea.toMap(describeDomainRecordsRequest)))
        let resp = await client.describeDomainRecords(describeDomainRecordsRequest);
        let queryResult = Util.toJSONString(resp.body.domainRecords)
        let resultObj = JSON.parse(queryResult);
        let domanResult = resultObj.record[0]
        console.log("处理ipv6----查询域名解析记录-结果:"+queryResult);
        changeResult.domain = domain
        changeResult.rr = key
        if(domanResult){
          changeResult.before = domanResult.value
          changeResult.after = ipv6
          //更新域名记录
          let updateDomainRecordRequest = new $Alidns20150109.UpdateDomainRecordRequest({
            TTL: 600,
            priority: 1,
            value: ipv6,
            type: "AAAA",
            RR: key,
            recordId: domanResult.recordId,
          });
          console.log("处理ipv6----更新域名解析记录-开始:"+Util.toJSONString($tea.toMap(updateDomainRecordRequest)))
          try {
            let updateResp = await client.updateDomainRecord(updateDomainRecordRequest);
            console.log("处理ipv6----更新域名解析记录-完成:"+Util.toJSONString($tea.toMap(updateResp)))
          } catch (error) {
            console.log("处理ipv6----更新域名解析记录-完成:本次ip没有改变:"+error)
          }
        } else {
          //尝试新增记录
          let addDomainRecordRequest = new $Alidns20150109.AddDomainRecordRequest({
            TTL: 600,
            priority: 1,
            value: ipv6,
            type: "AAAA",
            domainName: domain,
            RR: key
          });
          console.log("处理ipv6----新增域名解析记录-开始:"+Util.toJSONString($tea.toMap(addDomainRecordRequest)))
          let addResp = await client.addDomainRecord(addDomainRecordRequest);
          changeResult.before = "无"
          changeResult.after = ipv6
          changeResult.change = true
          console.log("处理ipv6----新增域名解析记录-完成:"+Util.toJSONString($tea.toMap(addResp)))
          Client.pushNotice(null,changeResult);
        }
      } else {
        console.error("处理ipv6----ipv6获取异常,跳过处理ipv4!")
      }
    })
    
  }

  /**
   * 
   * @returns 主方法
   */
  static async main() {
    console.log("----------------开始同步域名解析记录-----------------")
    let DDNS_ALI_AK = process.env.DDNS_ALI_AK
    let DDNS_ALI_SK = process.env.DDNS_ALI_SK
    if(!DDNS_ALI_AK || DDNS_ALI_AK == ""){
      console.log("请在环境变量里填写阿里云accessKey:DDNS_ALI_AK=")
      return
    }
    if(!DDNS_ALI_SK || DDNS_ALI_AK == ""){
      console.log("请在环境变量里填写阿里云secretKey:DDNS_ALI_SK=")
      return
    }
    let client = Client.createClient(DDNS_ALI_AK, DDNS_ALI_SK);
    let ipv6Domain = process.env.DDNS_IP6_DOMAIN ? process.env.DDNS_IP6_DOMAIN : ""
    if(ipv6Domain != ""){
       this.handleIpv6(client,ipv6Domain);
    }
    let ipv4Domain = process.env.DDNS_IP4_DOMAIN ? process.env.DDNS_IP4_DOMAIN : ""
    if(ipv4Domain != ""){
       this.handleIpv4(client,ipv4Domain);
    }
  }


  static pushNotice(changeIp4Result : any,changeIp6Result : any){
    let plshType = process.env.DDNS_PUSH_TYPE
    if(plshType){
      let content = PushUtil.html_template
      if(changeIp4Result != null && changeIp4Result.change){
        console.log("您的域名解析发生变更--ipv4变更:"+Util.toJSONString($tea.toMap(changeIp4Result)))
        let ip4Html = PushUtil.ip4_template.replace("IP4_KEY",changeIp4Result.rr).
        replace("IP4_CHANGE_BEFORE",changeIp4Result.before).
        replace("IP4_CHANGE_AFTER",changeIp4Result.after)
        content = content.replace("IP4_LINE",ip4Html)
        content = content.replace("IP6_LINE","").replace("IP4_LINE","");
        let title = "您的域名["+changeIp4Result.domain+"]解析发生变更!"
        PushUtil.pushConfigs.forEach(function(obj,index) {
          if(plshType == obj.type()){
            obj.push(title,content)
          }
        })
      }
      if(changeIp6Result != null && changeIp6Result.change){
        console.log("您的域名解析发生变更--ipv6变更:"+Util.toJSONString($tea.toMap(changeIp6Result)))
        let ip6Html = PushUtil.ip6_template.replace("IP6_KEY",changeIp6Result.rr).
        replace("IP6_CHANGE_BEFORE",changeIp6Result.before).
        replace("IP6_CHANGE_AFTER",changeIp6Result.after)
        content = content.replace("IP6_LINE",ip6Html)
        content = content.replace("IP6_LINE","").replace("IP4_LINE","");
        let title = "您的域名["+changeIp6Result.domain+"]解析发生变更!"
        PushUtil.pushConfigs.forEach(function(obj,index) {
          if(plshType == obj.type()){
            obj.push(title,content)
          }
        })
      }
    }
  }

}

//Client.getIpv4()
//Client.getIpv6()
//console.log(process.env.DDNS_IP4_DOMAIN)
Client.main()